/** Implementation of main.
 * @file 
 * @author Jeremy Schultz
 */
#include "main.h"

//-----------------------------------------------------------------------------
/** main.
 *  - Process the command line arguments (input, output, assembly)
 *  - Create the need classes (admin, symboltable, scanner, parser)
 *  - Starts the parser
 *  - If no errors output the requested (Pl interpreter or MIPS) assembly code
 *  - If requested output the intermediate code
 *  @param argc number of arguments
 *  @param argv See Usage string
 *  @return	scanner failed or not
 */
int main(int argc, char *argv[])	{
	// Initiate the Symbol Table with keywords
	SymbolTable* symbolTable; 

	Administration* admin;
	Scanner* scanner;
	Parser* parser;
	Assembler* assembler;
	AssemblerMIPS* assemblerMIPS;

	string outputFile = "pl.out";
	string intermediateFile;
	string usage = " [-i <intermediate file>] [-o <output file>] [-m] <source file>\n";
	usage+="	default <output file> is pl.out\n";
	usage+="	-m		output MIPS instructions\n";

	ifstream* fin;
	ofstream* fout;
	ofstream* fIntermediate = NULL;
	stringstream buffer;
	
	bool outputMIPS = false;
	int i;

	// Get the options
	for(i=1; i < argc; i++) {
		if (argv[i][0] != '-') {
			break;
		}

		switch (argv[i][1])		{
			case 'i': // intermediate file
				intermediateFile = argv[++i];
				break;

			case 'm': // Output MIPS
				outputMIPS = true;
				break;

			case 'o': // output file
				outputFile = argv[++i];
				break;

			case '?': // help 
			case 'h': // help 
				cout << "usage: " << argv[0] << usage << endl;
				exit(0);
				break;

			default:
				cerr << "Unknown option '"<< argv[i] << "'" << endl;
				cout << "usage: " << argv[0] << usage << endl;
				exit(-1);
		}
	}

	// Check if source file was specified
	if (i == argc) {
		cerr << "Missing <source file>" << endl;
		cout << "usage: " << argv[0] << usage << endl;
		exit(-1);
	}

	// Open the input file
	if ( ( fin = new ifstream(argv[i]) )->fail() ) {
		cerr << "Cannot open source file '"<< argv[i] << "'" << endl;
		exit(-2);
	}

	// Open the output file
	if ( ( fout = new ofstream( outputFile.c_str() ) )->fail() ) {
		cerr << "Cannot open output file '"<< outputFile << "'" << endl;
		exit(-2);
	}

	// Open the assembly file if needed
	if (!intermediateFile.empty() ) {
		if ( ( fIntermediate = new ofstream( intermediateFile.c_str() ) )->fail() ) {
			cerr << "Cannot open assembly file '"<< intermediateFile << "'" << endl;
			exit(-2);
		}
	}
	
	
	// Initiate 
	admin = new Administration(buffer);
	symbolTable = new SymbolTable();
	scanner = new Scanner(fin, symbolTable, admin);
	parser = new Parser(scanner, admin, symbolTable);
	assembler = new Assembler(buffer, *fout);
	assemblerMIPS = new AssemblerMIPS(buffer, *fout);
	

	// Start the parser 
	parser->start();

	if ( !admin->ErrorOccurred() ) {
		// First Pass
		if (outputMIPS) {
			assemblerMIPS->firstPass();
		}else {
			assembler->firstPass();
		}

		// Output intermediate if it was requested
		if (fIntermediate != NULL) {
			(*fIntermediate) << buffer.str();
			fIntermediate->close();
		}

		// Second Pass
		buffer.clear();
		buffer.seekg(0, ios::beg);
		if (outputMIPS) {
			assemblerMIPS->secondPass();
		}else {
			assembler->secondPass();
		}
	}


	// DEBUG, show the state of the symbol table
	#if defined(TEST_PARSER) || defined(DEBUG)
		cout << endl << "Symbol Table" << endl;
		symbolTable->dumpTable();
	#endif

	fin->close();
	fout->close();
	return admin->ErrorOccurred();
}
